﻿using Autofac;
using Domain.Models;
using GitSyncApp.IServices;
using GitSyncApp.Models;
using GitSyncApp.Models.Enums;
using GitSyncApp.Services.Jobs;
using Quartz;
using Quartz.Spi;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace GitSyncApp.Services
{
    public class SyncJobService : ISyncJobService
    {
        private readonly IScheduler scheduler;
        private readonly IDictionary<string, SyncJob> factory = new Dictionary<string, SyncJob>();

        public IReadOnlyDictionary<string, SyncJob> JobFactory => factory.ToDictionary(kv => kv.Key, kv => kv.Value);
        public bool? IsStarted => scheduler?.IsStarted;
        public bool? IsShutdown => scheduler?.IsShutdown;

        public SyncJobService(ILifetimeScope lifetimeScope)
        {
            scheduler = lifetimeScope.Resolve<ISchedulerFactory>().GetScheduler().Result;
            scheduler.JobFactory = lifetimeScope.Resolve<IJobFactory>();
        }

        public async Task<SyncJob> CreateAsync(GitLibaray git, CancellationToken cancellationToken = default)
        {
            var job = new SyncJob(git);

            if (factory.ContainsKey(git.RepositoryUrl.AbsoluteUri))
            {
                return factory.SingleOrDefault(x => x.Key == git.RepositoryUrl.AbsoluteUri).Value;
            }

            try
            {
                var jobDetail = JobBuilder.Create<GitLibrarySyncJob>()
                    .UsingJobData("id", git.Id)
                    .WithIdentity($"job_{job.Guid}", $"group_{job.Guid}")
                    .Build();

                job.Key = jobDetail.Key;

                var jobTrigger = TriggerBuilder.Create()
                    .WithIdentity($"trigger_{job.Guid}", $"group_{job.Guid}")
                    .WithSimpleSchedule(x => x.WithIntervalInSeconds(job.SyncGitLibrary.SyncIntervalSecond).RepeatForever())
                    .Build();

                await scheduler.ScheduleJob(jobDetail, jobTrigger, cancellationToken);

                job.Status = SyncJobStatus.Created;

                factory.Add(git.RepositoryUrl.AbsoluteUri, job);

                return job;
            }
            catch
            {
                job.Status = SyncJobStatus.Abort;

                return job;
            }
        }
        
        public async Task<Result> CreateCollectionAsync(IReadOnlyCollection<GitLibaray> gits, CancellationToken cancellationToken = default)
        {
            bool allSuccess = true;

            var builder = new StringBuilder();

            foreach(var git in gits)
            {
                var job = await CreateAsync(git, cancellationToken);
                if(job.Status == SyncJobStatus.Abort)
                {
                    allSuccess = false;
                    builder.AppendLine($"仓库{git.Name}对应的");
                }
            }

            if (allSuccess)
                return Result.Succeed("全部任务成功");
            else
                return Result.Failed(600, builder.ToString());
        }

        public async Task<Result> RemoveAsync(GitLibaray git, CancellationToken cancellationToken = default)
        {
            if (!factory.ContainsKey(git.RepositoryUrl.AbsoluteUri))
            {
                return Result.Failed(404, "任务不存在");
            }
            var job = factory.SingleOrDefault(x => x.Key == git.RepositoryUrl.AbsoluteUri).Value;

            try
            {
                await scheduler.DeleteJob(job.Key, cancellationToken);

                job.Status = SyncJobStatus.Deleted;

                return Result.Succeed();
            }
            catch (SchedulerException ex)
            {
                job.Status = SyncJobStatus.Abort;
                return Result.Failed(500, ex.Message);
            }
        }

        public async Task<Result> PauseAsync(GitLibaray git, CancellationToken cancellationToken = default)
        {
            if (!factory.ContainsKey(git.RepositoryUrl.AbsoluteUri))
            {
                return Result.Failed(404, "任务不存在");
            }
            var job = factory.SingleOrDefault(x => x.Key == git.RepositoryUrl.AbsoluteUri).Value;

            try
            {
                await scheduler.PauseJob(job.Key, cancellationToken);

                job.Status = SyncJobStatus.Paused;

                return Result.Succeed();
            }
            catch(Exception ex)
            {
                return Result.Failed(500, ex.Message);
            }
        }

        public async Task<Result> ResumeAsync(GitLibaray git, CancellationToken cancellationToken = default)
        {
            if (!factory.ContainsKey(git.RepositoryUrl.AbsoluteUri))
            {
                return Result.Failed(404, "任务不存在");
            }
            var job = factory.SingleOrDefault(x => x.Key == git.RepositoryUrl.AbsoluteUri).Value;

            try
            {
                await scheduler.ResumeJob(job.Key, cancellationToken);

                job.Status = SyncJobStatus.Started;

                return Result.Succeed();
            }
            catch(Exception ex)
            {
                return Result.Failed(500, ex.Message);
            }
        }

        public async Task<Result> PauseAllAsync(CancellationToken cancellationToken = default)
        {
            try
            {
                await scheduler.PauseAll(cancellationToken);

                foreach(var job in factory.Values)
                {
                    job.Status = SyncJobStatus.Paused;
                }

                return Result.Succeed();
            }
            catch(Exception ex)
            {
                return Result.Failed(500, ex.Message);
            }
        }

        public async Task<Result> ResumeAllAsync(CancellationToken cancellationToken = default)
        {
            try
            {
                await scheduler.ResumeAll(cancellationToken);

                foreach(var job in factory.Values)
                {
                    job.Status = SyncJobStatus.Started;
                }

                return Result.Succeed();
            }
            catch (Exception ex)
            {
                return Result.Failed(500, ex.Message);
            }
        }
        
        public async Task StartAsync(CancellationToken cancellationToken = default)
        {
            if (!scheduler.IsStarted)
            {
                await scheduler.Start(cancellationToken);
            }
        }

        public async Task StopAsync(CancellationToken cancellationToken = default)
        {
            if (!scheduler.IsShutdown)
            {
                await scheduler.Shutdown(cancellationToken);
            }
        }
    }
}
